﻿/*-
 * Copyright (c) 1982, 1986, 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)socketvar.h	8.1 (Berkeley) 6/2/93
 */

#include <sys/select.h>			/* for struct selinfo */

/*
 * Kernel structure per socket.
 * Contains send and receive buffer queues,
 * handle on protocol and pointer to protocol
 * private data and error information.
 */
struct socket {
	short	so_type;			/* socket支持的通信语义，由创建socket的进程指定。比如SOCK_STREAM、SOCK_DGRAM或SOCK_RAW */
	short	so_options;			/* 可以改变socket行为的标志集合，可以通过系统调用setsockopt()设置。比如 SO_REUSEADDR */
	short	so_linger;			/* 当关闭一条连接时，socket继续发送数据的拖延时间 */
	short	so_state;			/* socket内部状态标志，下面SS_开头的宏，比如SS_NBIO */
	caddr_t	so_pcb;				/* 指向当前socket所属协议控制块。在Net/3中有三个pcb头，struct inpcb rawinpcb/tcb/udb */
	struct	protosw *so_proto;	/* 协议处理函数集，在创建socket时，根据要创建的socket类型通过域头domains找到对应的协议处理函数集并指向 */
/*
 * Variables for connection queueing.
 * Socket where accepts occur is so_head in all subsidiary sockets.
 * If so_head is 0, socket is not related to an accept.
 * For head socket so_q0 queues partially completed connections,
 * while so_q is a queue of connections ready to be accepted.
 * If a connection is aborted and it has so_head set, then
 * it has to be pulled out of either so_q0 or so_q.
 * We allow connections to queue up based on current queue lengths
 * and limit on number of queued connections for this socket.
 */
/* 服务端监听socket通过队列的形式维护，客户端发来的socket连接请求 */
	struct	socket *so_head;	/* 指向监听socket */
	struct	socket *so_q0;		/* 监听socket维护的队列，存放还未完全建立(eg.tcp三次握手未完成)的连接 */
	struct	socket *so_q;		/* 监听socket维护的队列，存放已经建立(eg.tcp三次握手已完成)的连接 */
	short	so_q0len;			/* so_q0 队列长度 */
	short	so_qlen;			/* so_q 队列长度 */
	short	so_qlimit;			/* 监听socket维护连接队列容量，默认是5 */
	short	so_timeo;			/* 用作accept、connet和close处理期间的等待通道 */
	u_short	so_error;			/* 保存了最近一个系统调用执行的差错码 */
	pid_t	so_pgid;			/* 记录了要给发送SIGIO信号的进程或进程组id，SS_ASYNC标志被设置时使用。可以通过soo_ioctl设置或查看 */
	u_long	so_oobmark;			/* 标识在输入数据流中最近收到的带外数据的开始点 */

/* 下面对socket缓存，即 struct	sockbuf结构简称为sb */
	struct	sockbuf {
		struct	mbuf *sb_mb;	/* 指向待接收或发送mbuf链表的第一个mbuf */

		u_long	sb_mbcnt;		/* sb_mb指向的mbuf链表，实际占用的字节数(mbuf数量 * 每个mbuf的容量) */
		u_long	sb_cc;			/* sb_mb指向的mbuf链表，实际存储的字节数 */

		/* 以下三个值，在 PRU_ATTACH (创建scoket)或 setsocket(SO_SNDBUF/SO_RCVBUF)时设置 */
		u_long	sb_mbmax;		/* sb_mbcnt的最大值，不超过 SB_MAX */
		u_long	sb_hiwat;		/* 发送或接收数据时，sb_cc的最大阀值，用于调整socket的流控算法 */
		long	sb_lowat;		/* 发送或接收数据时，sb_cc的最小阀值，用于调整socket的流控算法 */

		short	sb_flags;		/* 接收或发送mbuf控制标志, 比如 SB_WANT */
		short	sb_timeo;		/* 用来限制一个进程在读写调用中被阻塞的时间 */

		struct	selinfo sb_sel;	/* process selecting read/write */
	} so_rcv, so_snd;			/* so_rcv/so_snd分别用来保存接收和发送数据的信息 */

#define	SB_MAX		(256*1024)	/* socket发送或接收缓存的默认最大字节数 */
#define	SB_LOCK		0x01		/* 某个进程已经对sb加锁，这个进程正在操作该sb */
#define	SB_WANT		0x02		/* 某个/些进程正在等待给sb加锁，当给sb加了锁的进程释放锁后，这些进程中最先被唤醒的一个立即给该sb加锁 */
#define	SB_WAIT		0x04		/* 一个进程正在等待接收数据或发送数据所需的缓存 */
#define	SB_SEL		0x08		/* 一个或多个进程正在选择这个缓存 */
#define	SB_ASYNC	0x10		/* 为这个缓存产生异步I/O信号 */
#define	SB_NOTIFY	(SB_WAIT|SB_SEL|SB_ASYNC)	/* 一个进程正在等待缓存的变化，如果缓存发生任何改变，用 wakeup通知该进程 */
#define	SB_NOINTR	0x40		/* 操作不可中断，信号不取消加锁请求 */

	caddr_t	so_tpcb;			/* Net/3不使用该字段 */

	/* 在Net/3中只有NFS使用下面两个字段。NFS是一个运行在内核中的应用，对于这种应用使用tsleep/wakeup机制是不合适的。
       在NFS添加socket时，给so_upcall绑定处理函数，当数据到达缓存时，通过so_upcall直接触发NFS输入处理。*/
	void	(*so_upcall) __P((struct socket *so, caddr_t arg, int waitf));
	caddr_t	so_upcallarg;		/* Arg for above */
};

/* Socket 状态，除了SS_ASYNC和SS_NBIO 可以被fcntl和ioctl系统调用直接修改，其它标志仅用于内核 */
#define	SS_NOFDREF				0x001	/* 仅用于内核，socket 没有与任何描述符相连 */
#define	SS_ISCONNECTED			0x002	/* 仅用于内核，socket 已经连接到对端，对应TCP收到ACK的状态 */
#define	SS_ISCONNECTING			0x004	/* 仅用于内核，socket 正在连接到对端，对应TCP发出SYN的状态 */
#define	SS_ISDISCONNECTING		0x008	/* 仅用于内核，socket 正在与对端断开连接 */
#define	SS_CANTSENDMORE			0x010	/* 仅用于内核，socket 不能再给对端发送数据 */
#define	SS_CANTRCVMORE			0x020	/* 仅用于内核，socket 不能再接收对端的数据 */
#define	SS_RCVATMARK			0x040	/* 仅用于内核，在最近的带外数据到达之前，socket已处理完所有收到的数据 */
#define	SS_PRIV					0x080	/* 仅用于内核，当前socket 由超级管理员创建，拥有广播等权限 */
#define	SS_NBIO					0x100	/* socket 操作不阻塞进程。socket 发出i/o请求且请求的资源不可用，不阻塞进程而是返回EWOULDBLOCK */
#define	SS_ASYNC				0x200	/* socket 异步i/o事件通知 */
#define	SS_ISCONFIRMING			0x400	/* 仅用于内核，socket 正在协商一个连接请求 */


/*
 * Macros for sockets and socket buffering.
 */

/*
* sb剩余可用空间
 * How much space is there in a socket buffer (so->so_snd or so->so_rcv)?
 * This is problematical if the fields are unsigned, as the space might
 * still be negative (cc > hiwat or mbcnt > mbmax).  Should detect
 * overflow and return 0.  Should use "lmin" but it doesn't exist now.
 */
#define	sbspace(sb) \
    ((long) imin((int)((sb)->sb_hiwat - (sb)->sb_cc), \
	 (int)((sb)->sb_mbmax - (sb)->sb_mbcnt)))

/* do we have to send all at once on a socket? */
#define	sosendallatonce(so) \
    ((so)->so_proto->pr_flags & PR_ATOMIC)

/* can we read something from so? */
#define	soreadable(so) \
    ((so)->so_rcv.sb_cc >= (so)->so_rcv.sb_lowat || \
	((so)->so_state & SS_CANTRCVMORE) || \
	(so)->so_qlen || (so)->so_error)

/* can we write something to so? */
#define	sowriteable(so) \
    (sbspace(&(so)->so_snd) >= (so)->so_snd.sb_lowat && \
	(((so)->so_state&SS_ISCONNECTED) || \
	  ((so)->so_proto->pr_flags&PR_CONNREQUIRED)==0) || \
     ((so)->so_state & SS_CANTSENDMORE) || \
     (so)->so_error)

/* 根据mbuf中的数据调整sb的实际字节数和存储字节数 */
#define	sballoc(sb, m) { \
	(sb)->sb_cc += (m)->m_len; \
	(sb)->sb_mbcnt += MSIZE; \
	if ((m)->m_flags & M_EXT) \
		(sb)->sb_mbcnt += (m)->m_ext.ext_size; \
}

/* 根据mbuf中的数据调整sb的实际字节数和存储字节数 */
#define	sbfree(sb, m) { \
	(sb)->sb_cc -= (m)->m_len; \
	(sb)->sb_mbcnt -= MSIZE; \
	if ((m)->m_flags & M_EXT) \
		(sb)->sb_mbcnt -= (m)->m_ext.ext_size; \
}

/*
 * 给sb加锁，保证同时只能有一个进程操作该sb。
 * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
 * Returns error without lock if sleep is interrupted.
 */
#define sblock(sb, wf) ((sb)->sb_flags & SB_LOCK ? \
		(((wf) == M_WAITOK) ? sb_lock(sb) : EWOULDBLOCK) : \
		((sb)->sb_flags |= SB_LOCK), 0)

/* 释放加在sb上的锁。所有等待给sb加锁的进程被唤醒。 */
#define	sbunlock(sb) { \
	(sb)->sb_flags &= ~SB_LOCK; \
	if ((sb)->sb_flags & SB_WANT) { /* 当前进程释放对sb的锁后，发现有进程在等待给sb加锁希望操作该sb，使用wakeup唤醒所有等待进程 */ \ 
		(sb)->sb_flags &= ~SB_WANT; \
		wakeup((caddr_t)&(sb)->sb_flags); \
	} \
}

/* 唤醒等待在读sb的所有进程 */
#define	sorwakeup(so)	{ sowakeup((so), &(so)->so_rcv); \
			  if ((so)->so_upcall) \
			    (*((so)->so_upcall))((so), (so)->so_upcallarg, M_DONTWAIT); \
			}

/* 唤醒等待在写sb的所有进程 */
#define	sowwakeup(so)	sowakeup((so), &(so)->so_snd)

#ifdef KERNEL
u_long	sb_max;
/* to catch callers missing new second argument to sonewconn: */
#define	sonewconn(head, connstatus)	sonewconn1((head), (connstatus))
struct	socket *sonewconn1 __P((struct socket *head, int connstatus));

/* strings for sleep message: */
extern	char netio[], netcon[], netcls[];

/*
 * File operations on sockets.
 */
int	soo_read __P((struct file *fp, struct uio *uio, struct ucred *cred));
int	soo_write __P((struct file *fp, struct uio *uio, struct ucred *cred));
int	soo_ioctl __P((struct file *fp, int com, caddr_t data, struct proc *p));
int	soo_select __P((struct file *fp, int which, struct proc *p));
int 	soo_close __P((struct file *fp, struct proc *p));
#endif
